package fr.teuteuf.runstickmanrun.entity;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.World;

import fr.teuteuf.runstickmanrun.Animation;
import fr.teuteuf.runstickmanrun.Constants;
import fr.teuteuf.runstickmanrun.IRenderable;
import fr.teuteuf.runstickmanrun.IUpdatable;

public abstract class AbstractEntity implements IUpdatable, IRenderable {
	
	protected Body body;
	protected Vector2 position = new Vector2(0, 0);
	protected Vector2 velocity = new Vector2(0, 0);
	protected Animation animations[];
	protected Animation currentAnimation;
	
	protected float jumpForce;
	protected float movingSpeed;
	protected int percentAcceleration;
	private boolean isFlipped;
	private boolean isMovingRight = false, isMovingLeft = false, isJumping = false, isDoubleJumping = false, isDead = false, isSprinting;
	
	public void update(float elapsedTime) {
		float acceleration = (isSprinting) ? movingSpeed * percentAcceleration / 100 : 0;
		position = body.getPosition();
		
		Vector2 tempVelocity = body.getLinearVelocity();
		if(isMovingRight && !isMovingLeft && !isDead) {
			body.setLinearVelocity(movingSpeed + acceleration, tempVelocity.y);
		} else if(!isMovingRight && isMovingLeft && !isDead) {
			body.setLinearVelocity(- (movingSpeed + acceleration), tempVelocity.y);
		} else {
			body.setLinearVelocity(0, tempVelocity.y);
		}
		
		velocity = body.getLinearVelocity();
		
		if(velocity.x < -0.1f) {
			isFlipped = true;
		} else if(velocity.x > 0.1f) {
			isFlipped = false;
		}

		updateCurrentAnimation();
		
		if(Math.abs(velocity.y) > 0.01f) {
			isJumping = true;
		} else {
			isJumping = false;
		}
		
		currentAnimation.update(elapsedTime);
	}
	
	protected abstract void initAnimations();
	protected abstract void initBody(World world, int x, int y);
	public abstract void updateCurrentAnimation();
	
	@Override
	public void render(SpriteBatch spriteBatch) {
		TextureRegion currentTexture = currentAnimation.getCurrentFrameTexture();
		
		if(isFlipped && ! currentTexture.isFlipX() || !isFlipped && currentTexture.isFlipX()) {
			currentTexture.flip(true, false);
		}
		
		spriteBatch.draw(
				currentTexture, 
				position.x * Constants.BOX_TO_WORLD - currentTexture.getRegionWidth() / 2, 
				position.y * Constants.BOX_TO_WORLD - currentTexture.getRegionHeight() / 2);
	}
	
	/**
	 * @param b, true = launch the action, false = cancel the action
	 */
	public void jump(boolean b) {
		if(b && ! isJumping && ! isDead) {
			body.setLinearVelocity(velocity.x, 0);
			body.applyForceToCenter(0, jumpForce);
			isJumping = true;
		} else if(b && ! isDoubleJumping && ! isDead && velocity.y < 0f) {
			body.setLinearVelocity(velocity.x, 0);
			body.applyForceToCenter(0, jumpForce);
			isDoubleJumping = true;
		} else {
			if(velocity.y > 0f) {
				body.setLinearVelocity(velocity.x, 0);
			}
		}
	}
	
	/**
	 * @param b, true = launch the action, false = cancel the action
	 */
	public void moveLeft(boolean b) {
		isMovingLeft = b;
	}
	
	/**
	 * @param b, true = launch the action, false = cancel the action
	 */
	public void moveRight(boolean b) {
		isMovingRight = b;
	}
	
	public void sprint(boolean b) {
		isSprinting = b;
	}
	
	public void setPosition(float x, float y) {
		body.setTransform(x, y, 0);
	}
	
	public Vector2 getPosition() {
		return body.getPosition();
	}

	public boolean isJumping() {
		return isJumping;
	}

	public void setJumping(boolean isJumping) {
		this.isJumping = isJumping;
	}
	
	public boolean isDoubleJumping() {
		return isDoubleJumping;
	}
	
	public void setDoubleJumping(boolean isDoubleJumping) {
		this.isDoubleJumping = isDoubleJumping;
	}
	
	public boolean isSprinting() {
		return isSprinting;
	}

	public void setSprinting(boolean isSprinting) {
		this.isSprinting = isSprinting;
	}

	public void kill() {
		isDead = true;
	}
	
	public boolean isDead() {
		return isDead;
	}
}
